#include "pch.h"
#include "CameraManager.h"

CameraManager::CameraManager (u32 w, u32 h, fp32 fov) {
	SDL_GetWindowSize(g_window, (int *)(&_window_width), (int *)(&_window_height));
	_pos.set(0.f, 0.f, 0.f);
	_target.set(0.f, 0.f, 1.f);
	_up.set(0.f, 1.f, 0.f);

	init();
}

void CameraManager::init () {
	Vector3f h_target(_target.x(), 0.f, _target.z());
	h_target.nrm();

	if (h_target.z() >= 0.f) {
		if (h_target.x() >= 0.f) {
			_angle_h = 360.f - TO_DEGREES(asin(h_target.z()));
		} else {
			_angle_h = 180.f + TO_DEGREES(asin(h_target.z()));
		}
	} else {
		if (h_target.x() >= 0.f) {
			_angle_h = TO_DEGREES(asin(-h_target.z()));
		} else {
			_angle_h = 90.f + TO_DEGREES(asin(-h_target.z()));
		}
	}

	_angle_v = -TO_DEGREES(asin(_target.y()));

	_mouse_pos.set(_window_width / 2, _window_height / 2);

	SDL_WarpMouseInWindow(g_window, _mouse_pos.x(), _mouse_pos.y());

	_need_mouse_update = false;
}

void CameraManager::update () {
	const Vector3f v_axis(0.f, 1.f, 0.f);

	// view vector rotation by the horiz. angle around vert. axis
	Vector3f view(1.f, 0.f, 0.f);
	view.rotate(_angle_h, v_axis);
	view.nrm();

	// view vector rotation by the vert. angle around the horiz. axis
	Vector3f h_axis(Vector3f::cross(v_axis, view));
	h_axis.nrm();
	view.rotate(_angle_v, h_axis);
	view.nrm();

	_target = view;
	_target.nrm();

	_up = Vector3f::cross(_target, h_axis);
	_up.nrm();

}

void CameraManager::onRender () {
}

const Matrix_4x4 &CameraManager::initCameraTranslation () {
	Vector3f N = _target;
	N.nrm();
	Vector3f U = _up;
	U.nrm();
	U = Vector3f::cross(U, _target);
	Vector3f V = Vector3f::cross(N, U);

	_camera_rotation.setIdentity();
	_camera_rotation.set(0, 0, U.x());
	_camera_rotation.set(1, 0, V.x());
	_camera_rotation.set(2, 0, N.x());
	_camera_rotation.set(0, 1, U.y());
	_camera_rotation.set(1, 1, V.y());
	_camera_rotation.set(2, 1, N.y());
	_camera_rotation.set(0, 2, U.z());
	_camera_rotation.set(1, 2, V.z());
	_camera_rotation.set(2, 2, N.z());
	return _camera_rotation;
}

const Matrix_4x4 &CameraManager::initCameraRotation () {
	_camera_translation.setIdentity();
	_camera_translation.translate(Vector3f(-_pos.x(), -_pos.y(), -_pos.z()));
	return _camera_translation;
}

bool CameraManager::keyboardHandler (unsigned char Key) {
	bool Ret = true;
	switch (Key) {
	case 'W' :
	case 'w' :
		{
			_pos.add(Vector3f::mul(_target, STEP));
		}
		break;
	case 'S' :
	case 's' :
		{
			_pos.sub(Vector3f::mul(_target, STEP));
		}
		break;
	case 'A' :
	case 'a' :
		{
			Vector3f Left = Vector3f::cross(_target, _up);
			Left.nrm();
			Left.mul(STEP);
			_pos.add(Left);
		}
		break;
	case 'D' :
	case 'd' :
		{
			Vector3f Right = Vector3f::cross(_up, _target);
			Right.nrm();
			Right.mul(STEP);
			_pos.add(Right);
		}
		break;
	case 'M' :
	case 'm' :
		{
			_need_mouse_update = !_need_mouse_update;
			_mouse_pos.set(_window_width / 2, _window_height / 2);
			SDL_WarpMouseInWindow(g_window, _mouse_pos.x(), _mouse_pos.y());
		}
		break;
	case SDLK_ESCAPE: 
		return false;
	}
	return Ret;
}

void CameraManager::mouseHandler () {
	int x, y;
	SDL_GetMouseState(&x, &y);
	if (!need_mouse_update()) return;

	const int dx = x - _mouse_pos.x();
	const int dy = y - _mouse_pos.y();

	_mouse_pos.set(x, y);

	_angle_h += (fp32)dx;
	_angle_v += (fp32)dy;

	update();
}